/*
 * machine-independent include file
 * $Id: decomp.h,v 1.22 2012/07/21 00:05:04 curt Exp $
 */

#ifndef DECOMP_H
#define	DECOMP_H

extern void *malloc();

typedef unsigned long long uint64;
typedef unsigned long long int64;
typedef unsigned long uint32;
typedef unsigned long int32;

/* forward declarations */
typedef struct section_s section;
typedef struct label_s label;
typedef struct symbol_s symbol;
typedef struct reference_s reference;
typedef struct instruction_s inst;
typedef struct expr_s expr;
typedef struct code_s code;
typedef struct function_s function;
typedef struct regfile_s regfile;
typedef enum code_e code_e;
typedef enum operator_e operator_e;

char *format_binary(unsigned int, int, unsigned int);
label *lookup_label(section *s, int offset);
label *make_label(section *sp, int off, char *name);
inst *lookup_inst(section *s, int offset);
inst *make_inst(section *s, int offset);
function *make_function(label *name);
code *make_code(code_e type);
reference *lookup_reference(section *s, int offset, int index);
reference *make_reference(section *sp, int off, int ind, label *l);
regfile *make_regfile();
regfile *clone_regs(regfile *rp);
inst *crack_inst(section *sp, int offset, int *bytes);
int extend(int val, int bits);
expr *make_expr(operator_e type, expr *l, expr *r, label *lp, uint64 cv);
expr *ref_expr(reference *rp);
expr *dest_expr(inst *ip);
inst *crack_code(section *sp, int offset);
expr *fold(expr *e);

void dump_inst(inst *ip);

function *funcs;
regfile *lastregs;
int verbose;
int imax;

#define VERBOSE_FUNC    0x1     /* all function calls */
#define VERBOSE_ANAL    0x2     /* data analysis */
#define VERBOSE_OBJ     0x4     /* object file */
#define VERBOSE_INST    0x8     /* instruction decode */
#define VERBOSE_LOW     0x10    /* low level */
#define VERBOSE_FLOW    0x20    /* flow */
#define VERBOSE_FOLD    0x40    /* fold */
#define VERBOSE_CALC    0x80    /* calc */

char *regname[];

#define ASSERTS

void assert_fail();

#ifdef ASSERTS
#define ASSERT(x) { if (!x) { assert_fail(#x); } }
#else
#define ASSERT(x)
#endif

/*
 * data structures.
 * we extract the relevant information from the object file and build a graph
 * of this stuff.
 *
 * bfd duplicates a lot of this, but since we need to work on raw binaries,
 * we synthesize this stuff from all our sources of data, like from the
 * instructions themselves
 *
 * every pointer in these things is malloc'ed, and we never reference
 * bfd data structures in these.
 *
 * there's a tremendous amount of redundancy in here, graph-wise.
 * this is for verifications's sake.
 */

/*
 * a section is an address space.
 * it may have contents, or may not, like bss.
 */
struct section_s {
    char *name;

    int text;			/* contains instructions */
    char prefix;		/* generated symbol prefix */

    /* the unstructured instructions and data */
    unsigned char *data;
    int length;

    /* all the labels referring to addresses in this section */
    label **labels;

    /* all the references from here to addresses anywhere */
    reference **refs;

    /* the parsed instructions */
    inst **insts;

    section *next;      /* the next section */
};

/*
 * a visible address.
 * we will make sure there is one of these for every reference
 * there is at most one of these for each section and offset
 */
struct label_s {
    /* unique */
    section *sect;
    int offset;

    char *name;         /* how we refer to it in code */
    inst *inst;         /* if present */

    reference *refs;	/* all cross references */

    label *g_next;		/* next label globally */
    label *s_next;		/* next label in section */
    int generated;      /* not external */
};

/*
 * a reference
 * this is place in a section and offset where we reference another address.
 * we link all references to the same label together.
 * note that we may have more than one reference from a single instruction,
 * *IF* we have a multi-address machine.
 */
struct reference_s {
    /* unique */
    section *sect;
    int offset;

    inst *inst;         /* what instruction */
    label *place;		/* what we reference */
    char howcode;       /* how we reference it - bfd */
    char index;

    reference *l_next;	/* refs to same label */
    reference *s_next;	/* refs in same section */
};

/*
 * an instruction
 * this is a hunk of storage that contains an operation,
 * various flags, and a number of references (usually at most one)
 * may have a label, if it is referenced elsewhere
 */
struct instruction_s {
    /* unique */
    section *sect;      /* where we are */
    int offset;         /* our address */

    label *addr;        /* if we are visible */

#define MAXREFS 3
    reference *ref[MAXREFS];     /* we reference to another place */

    inst *next;         /* next straightline */
    inst *branch;       /* if conditional branch */

    unsigned int opcode;
    unsigned char type;
    unsigned char width;
    unsigned char extend;

    expr *calc;         /* what we calculate */

#define MAXATTR 16
    unsigned int attr[MAXATTR];
    regfile *state;

#define MAXFLOWS 32
    inst *flow[MAXFLOWS];
    int refcnt;         /* number of flows into this instruction */

    function *func;
};

#define MAXREGS 32

/*
 * a register file scoreboard.
 * most instructions either create, destroy or update one of these.
 */
struct regfile_s {
    expr *reg[MAXREGS];
    regfile *saved;
};

enum operator_e {
    O_REG,      /* the goal is to eliminate all of these */

    O_CONST,
    O_SYMBOL,
    O_RETADDR,
    O_STACK,
    O_ARG,

    O_LOAD,     /* unary */
    O_NOT,
    O_NEG,

    O_OR,       /* binary */
    O_AND,
    O_XOR,

    O_ADD,
    O_SUB,
    O_MUL,
    O_DIV,

    O_LSHIFT,
    O_RSHIFT,
    O_LAND,
    O_LOR,
    O_LNOT,
};

/*
 * a value expression, intermediate result of a calculation
 */
struct expr_s {
    int width;
    uint64 val;
    uint64 mask;
    operator_e op;
    label *symbol;      /* valid if O_SYMBOL */
    expr *left;         /* valid if takes argument */
    expr *right;        /* valid if binary operator */

    expr *next;

};

/*
 * a code node
 * code is really only 3 possible things:
 * a function call,
 * a store into memory
 * or a conditional branch
 */
enum code_e {
    C_CALL,
    C_STORE,
    C_IF
};

struct code_s {
    code_e type;
    expr *arg1;
    expr *arg2;
    code *next;
};

struct function_s {
    label *label;

    int argcount;
    expr *args;
    expr *ret;

    inst *tree;

    code *code;
    code *tail;

    section *sect;
    function *next;
};

/*
 * condition
 */
struct condtype {
    int type;
    char **condnames;
    char **flags;
};

#endif	/* DECOMP_H */
